package controller;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.swing.JDialog;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import util.HibernateUtil;
import view.BorrowBookPage;
import view.BorrowReturnBookPage;
import view.LendBookPage;
import view.ReturnBookPage;
import bean.Catalog;
import bean.Loan;
import bean.User;

import com.mysql.jdbc.StringUtils;

import dto.BookDTO;
import dto.BorrowBookDTO;
import dto.LendBookDTO;
import dto.LogonDTO;
import dto.ReturnBookDTO;
import dto.UserLoanBookDTO;

public class BorrowReturnBookController {

	private BorrowReturnBookPage borrowReturnBook;
	private BorrowBookPage borrowBook;
	private LendBookPage lendBook;
	private ReturnBookPage returnBook;

	private CommonBookController commonController;
	private ViewBookController viewController;

	private BorrowBookDTO borrowDTO;
	private ReturnBookDTO returnDTO;
	private String userName;

	void borrowReturnBook(LogonDTO logonDTO) {
		userName = logonDTO.getUserName();

		borrowReturnBook = new BorrowReturnBookPage(logonDTO);

		borrowReturnBook.addBorrowBookButtonListener(new BorrowBookListener());
		borrowReturnBook.addReturnBookButtonListener(new ReturnBookListener());

		borrowReturnBook.setModal(true);
		borrowReturnBook.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		borrowReturnBook.setVisible(true);

	}

	void borrowBook(LogonDTO logonDTO, BorrowBookDTO borrowBookDTO) {
		this.borrowDTO = borrowBookDTO;

		borrowBook = new BorrowBookPage(logonDTO, borrowDTO);

		borrowBook.addViewBookButtonListener(new ViewBookListener());
		borrowBook.addLendBookButtonListener(new ActionListener() {

			public void actionPerformed(ActionEvent arg0) {

				borrowDTO = borrowBook.getBorrowBookDTO();
				String ddc = borrowDTO.getBookDTO().getDeweyDecimalNo();
				BookDTO bookDTO = commonController.populateBookDTO(ddc);

				if (bookDTO != null) {

					if ("N".equalsIgnoreCase(bookDTO.getOnLoan())) {
						borrowDTO.setBookDTO(bookDTO);
						lendBook(borrowBook.getLogonDTO(), borrowDTO);
					} else {
						borrowBook
								.displayMessage("Book is not available for lending.");
					}

				} else {
					borrowBook
							.displayMessage("Catalog Number (DDC) unrecognized: "
									+ ddc);
				}
			}
		});

		List<Loan> loanList = commonController.fetchLoanByUser(borrowDTO
				.getBorrowerId());
		List<UserLoanBookDTO> userLoanList = commonController
				.populateUserLoanBookDTO(loanList);
		borrowBook.setLoanedBooks(userLoanList);

		borrowBook.reDrawThis(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {

				int index = Integer.parseInt(arg0.getActionCommand());
				UserLoanBookDTO userLoanBookDTO = borrowBook.getLoanedBooks()
						.get(index);

				viewController.viewBook(borrowBook.getLogonDTO(),
						userLoanBookDTO.getBookDTO());
			}
		});

		borrowBook.setModal(true);
		borrowBook.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		borrowBook.setVisible(true);
	}

	void lendBook(LogonDTO logonDTO, BorrowBookDTO borrowBookDTO) {
		this.borrowDTO = borrowBookDTO;
		this.userName = logonDTO.getUserName();

		lendBook = new LendBookPage(logonDTO, borrowBookDTO);
		lendBook.addLendBookButtonListener(new LendBookListener());

		lendBook.setModal(true);
		lendBook.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		lendBook.setVisible(true);

	}

	void returnBook(LogonDTO logonDTO, ReturnBookDTO returnBookDTO) {
		this.returnDTO = returnBookDTO;

		returnBook = new ReturnBookPage(logonDTO, returnBookDTO);

		String returnerId = returnDTO.getReturnerId();
		String ddc = returnDTO.getDeweyDecimalNo();
		String errorMsg = null;

		List<UserLoanBookDTO> userLoanList = new ArrayList<UserLoanBookDTO>();

		if (!StringUtils.isNullOrEmpty(returnerId)) {
			errorMsg = "No borrowed book.";

			List<Loan> loanList = commonController.fetchLoanByUser(returnerId);
			userLoanList = commonController.populateUserLoanBookDTO(loanList);

		} else if (!StringUtils.isNullOrEmpty(ddc)) {
			List<Loan> loanList = fetchLoanByDDC(ddc);
			userLoanList = commonController.populateUserLoanBookDTO(loanList);

		}

		if (userLoanList.size() != 0) {
			returnBook.setLoanedBooks(userLoanList);

			returnBook.reDrawThis(new ActionListener() {
				public void actionPerformed(ActionEvent arg0) {

					int index = Integer.parseInt(arg0.getActionCommand());
					UserLoanBookDTO userLoanBookDTO = returnBook
							.getLoanedBooks().get(index);

					viewController.viewBook(returnBook.getLogonDTO(),
							userLoanBookDTO.getBookDTO());
				}
			}, new ReturnABookListener());
		} else {
			returnBook.displayMessage(errorMsg);
		}

		returnBook.setModal(true);
		returnBook.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
		returnBook.setVisible(true);

	}

	class ViewBookListener implements ActionListener {

		public void actionPerformed(ActionEvent e) {
			borrowDTO = borrowBook.getBorrowBookDTO();
			String ddc = borrowDTO.getBookDTO().getDeweyDecimalNo();

			BookDTO bookDTO = commonController.populateBookDTO(ddc);

			if (bookDTO != null) {
				viewController.viewBook(borrowBook.getLogonDTO(), bookDTO);

			} else {
				borrowBook.displayMessage("Catalog Number (DDC) unrecognized: "
						+ ddc);
			}
		}
	}

	class BorrowBookListener implements ActionListener {

		public void actionPerformed(ActionEvent arg0) {
			LogonDTO logonDTO = borrowReturnBook.getLogonDTO();
			borrowDTO = borrowReturnBook.getBorrowBookDTO();

			if (!StringUtils.isNullOrEmpty(borrowDTO.getBorrowerId())) {
				User user = getMemberInfo(borrowDTO.getBorrowerId());

				if (user != null) {
					borrowDTO.setBorrowerName(user.getUserName());
					borrowBook(logonDTO, borrowDTO);
				} else {
					borrowReturnBook
							.displayBorrowMessage("Invalid borrower id: "
									+ borrowDTO.getBorrowerId());
				}
			} else {
				borrowReturnBook
						.displayBorrowMessage("Input borrower's username");
			}
		}
	}

	class LendBookListener implements ActionListener {

		public void actionPerformed(ActionEvent e) {
			LendBookDTO lendBookDTO = lendBook.getLendBookDTO();

			addBookLoanEntry(lendBookDTO);

			lendBook.displayMessage(borrowDTO.getBookDTO().getTitle()
					+ " is successfully lent to " + borrowDTO.getBorrowerId());
		}
	}

	class ReturnBookListener implements ActionListener {

		public void actionPerformed(ActionEvent arg0) {
			LogonDTO logonDTO = borrowReturnBook.getLogonDTO();
			returnDTO = borrowReturnBook.getReturnBookDTO();

			String returnerId = returnDTO.getReturnerId();
			String ddc = returnDTO.getDeweyDecimalNo();

			if (!StringUtils.isNullOrEmpty(returnerId)) {

				User user = getMemberInfo(returnDTO.getReturnerId());

				if (user != null) {
					returnDTO.setReturnerName(user.getUserName());
					returnBook(logonDTO, returnDTO);

				} else {
					borrowReturnBook
							.displayReturnMessage("Invalid returner id: "
									+ returnerId);
				}
			} else if (!StringUtils.isNullOrEmpty(ddc)) {

				Catalog catalogBean = commonController.fetchCatalogByDDC(ddc);

				if (catalogBean != null) {

					List<Loan> loanList = fetchLoanByDDC(ddc);

					if (loanList.size() != 0) {
						User user = getMemberInfo(loanList.get(0).getUserId());

						returnDTO.setReturnerId(user.getId());
						returnDTO.setReturnerName(user.getUserName());
						returnBook(logonDTO, returnDTO);
						returnBook(logonDTO, returnDTO);

					} else {
						borrowReturnBook
								.displayReturnMessage("The book doesn't seem to be borrowed by any user.");
					}
				} else {
					borrowReturnBook
							.displayReturnMessage("Catalog Number (DDC) unrecognized: "
									+ ddc);
				}
			} else {
				borrowReturnBook
						.displayReturnMessage("Input borrower's username or catalog number");
			}
		}
	}

	class ReturnABookListener implements ActionListener {

		public void actionPerformed(ActionEvent arg0) {

			ReturnBookDTO returnBookDTO = returnBook.getReturnBookDTO();

			int index = Integer.parseInt(arg0.getActionCommand());
			UserLoanBookDTO userLoanBookDTO = returnBook.getLoanedBooks().get(
					index);
			updateReturnBookLoanEntry(userLoanBookDTO,
					returnBookDTO.getReturnerId());

			returnBook.displayMessage("Book succesfully returned.");

			String returnerId = returnBookDTO.getReturnerId();
			String ddc = returnBookDTO.getDeweyDecimalNo();

			List<UserLoanBookDTO> userLoanList = new ArrayList<UserLoanBookDTO>();

			if (!StringUtils.isNullOrEmpty(returnerId)) {

				List<Loan> loanList = commonController
						.fetchLoanByUser(returnerId);
				userLoanList = commonController
						.populateUserLoanBookDTO(loanList);

			} else if (!StringUtils.isNullOrEmpty(ddc)) {

				List<Loan> loanList = fetchLoanByDDC(ddc);
				userLoanList = commonController
						.populateUserLoanBookDTO(loanList);
			}

			if (userLoanList.size() != 0) {
				returnBook.setLoanedBooks(userLoanList);

				returnBook.reDrawThis(new ActionListener() {
					public void actionPerformed(ActionEvent arg0) {

						int index = Integer.parseInt(arg0.getActionCommand());
						UserLoanBookDTO userLoanBookDTO = returnBook
								.getLoanedBooks().get(index);

						viewController.viewBook(returnBook.getLogonDTO(),
								userLoanBookDTO.getBookDTO());
					}
				}, new ReturnABookListener());
			}
		}
	}

	private void addBookLoanEntry(LendBookDTO lendBookDTO) {

		// update Catalog.onLoan="Y"
		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.openSession();
		Transaction transaction = null;

		Catalog catalogBean = commonController.fetchCatalogByDDC(lendBookDTO
				.getDeweyDecimalNo());
		catalogBean.setOnLoan("Y");
		catalogBean.setEditedBy(userName);
		catalogBean.setEditedDate(getCurrentTime());

		transaction = session.beginTransaction();
		session.saveOrUpdate(catalogBean);
		transaction.commit();

		// insert entry in Loan
		Loan loanBean = new Loan();

		loanBean.setCatalog(catalogBean);
		loanBean.setUserId(lendBookDTO.getBorrowerId());

		Date dueDate = lendBookDTO.getDueDate();
		loanBean.setLoanDate(lendBookDTO.getLoanDate());
		loanBean.setDueDate(dueDate);
		loanBean.setIsExtended("N");
		loanBean.setIsReturned("N");
		loanBean.setRemarks(lendBookDTO.getRemarks());
		loanBean.setEditedBy(userName);
		loanBean.setEditedDate(getCurrentTime());

		transaction = session.beginTransaction();
		session.save(loanBean);
		transaction.commit();
	}

	private void updateReturnBookLoanEntry(UserLoanBookDTO userLoanBookDTO,
			String returnerId) {

		BookDTO selectedToReturnBook = userLoanBookDTO.getBookDTO();

		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.openSession();
		Transaction transaction = null;

		Catalog catalogBean = commonController
				.fetchCatalogByDDC(selectedToReturnBook.getDeweyDecimalNo());

		// update entry in Loan
		Loan loanBean = fetchLoanByPrimaryKeys(catalogBean.getId(), returnerId,
				userLoanBookDTO.getLoanDate());

		loanBean.setIsReturned("Y");
		loanBean.setInDate(getCurrentTime());
		loanBean.setEditedBy(userName);
		loanBean.setEditedDate(getCurrentTime());

		transaction = session.beginTransaction();
		session.saveOrUpdate(loanBean);
		transaction.commit();

		// update Catalog.onLoan="N"
		catalogBean.setOnLoan("N");
		catalogBean.setEditedBy(userName);
		catalogBean.setEditedDate(getCurrentTime());

		transaction = session.beginTransaction();
		session.saveOrUpdate(catalogBean);
		transaction.commit();
	}

	private User getMemberInfo(String userName) {
		User user = null;

		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.openSession();

		Query query = session.createQuery(" FROM User WHERE userId = '"
				+ userName + "'");

		if (!query.list().isEmpty()) {
			user = (User) query.list().get(0);
		}
		return user;
	}

	private List<Loan> fetchLoanByDDC(String ddc) {
		List<Loan> searchResult = new ArrayList<Loan>();

		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.openSession();

		Query query = session
				.createQuery("SELECT loan FROM Loan loan LEFT JOIN FETCH loan.catalog "
						+ "WHERE loan.isReturned = 'N' AND loan.catalog.deweyDecimalNo = '"
						+ ddc + "'");

		List loanList = query.list();
		if (!loanList.isEmpty()) {
			Loan loan = (Loan) loanList.get(0);
			searchResult.add(loan);
		}

		return searchResult;
	}

	private Loan fetchLoanByPrimaryKeys(int catalogId, String userId,
			Date loanDate) {
		Loan loanBean = null;

		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.openSession();

		Query query = session
				.createQuery("SELECT loan FROM Loan loan LEFT JOIN FETCH loan.catalog WHERE loan.catalog.id = "
						+ catalogId
						+ " AND loan.userId = '"
						+ userId
						+ "' AND loan.loanDate = '" + loanDate + "'");

		if (!query.list().isEmpty()) {
			loanBean = (Loan) query.list().get(0);
		}

		return loanBean;
	}

	private Timestamp getCurrentTime() {
		java.util.Date date = new java.util.Date(System.currentTimeMillis());
		java.sql.Timestamp timestamp = new java.sql.Timestamp(date.getTime());

		return timestamp;
	}

}
